/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/19 23:53
  *
  * @file       pd_dma.c
  * @brief      DMA peripheral driver.
  *
  * @note       This file contains the peripheral driver for the DMA.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date          Author       Notes
  *   2024/6/19     Chris        the first version
  *
  ******************************************************************************
  */


#include <stm32f4xx_ll_bus.h>
#include <stm32f4xx_ll_dma.h>
#include "pd_dma.h"

static void DMA_init(DMA *this) {
    if      (this->DMAx == DMA1) LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
    else if (this->DMAx == DMA2) LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2);

    // DMA init
    LL_DMA_InitTypeDef DMA_InitStruct = {0};
    LL_DMA_StructInit(&DMA_InitStruct);

    DMA_InitStruct.PeriphOrM2MSrcAddress  = this->PeriphOrM2MSrcAddress;  // DMA_SxPAR  Bits 31:0 PAR[31:0]: Peripheral address
    DMA_InitStruct.MemoryOrM2MDstAddress  = this->MemoryOrM2MDstAddress;  // DMA_SxM0AR Bits 31:0 M0A[31:0]: Memory 0 address
    DMA_InitStruct.Direction              = this->Direction;              // DMA_SxCR   Bits 7:6 DIR[1:0]: Data transfer direction
    DMA_InitStruct.Mode                   = this->Mode;                   // DMA_SxCR   Bit 8 CIRC: Circular mod
    DMA_InitStruct.PeriphOrM2MSrcIncMode  = this->PeriphOrM2MSrcIncMode;  // DMA_SxCR   Bit 9 PINC: Peripheral increment mode
    DMA_InitStruct.MemoryOrM2MDstIncMode  = this->MemoryOrM2MDstIncMode;  // DMA_SxCR   Bit 10 MINC: Memory increment mode
    DMA_InitStruct.PeriphOrM2MSrcDataSize = this->PeriphOrM2MSrcDataSize; // DMA_SxCR Bits 12:11 PSIZE[1:0]: Peripheral data size
    DMA_InitStruct.MemoryOrM2MDstDataSize = this->MemoryOrM2MDstDataSize; // DMA_SxCR Bits 14:13 MSIZE[1:0]: Memory data size
    DMA_InitStruct.NbData                 = this->NbData;                 // DMA_SxNDTR Bits 15:0 NDT[15:0]: Number of data items to transfer
    DMA_InitStruct.Channel                = this->Channel;                // DMA_SxCR   Bits 27:25 CHSEL[2:0]: Channel selection
    DMA_InitStruct.Priority               = this->Priority;               // DMA_SxCR   Bit 5 PFCTRL: Peripheral flow controller

    DMA_InitStruct.FIFOMode               = this->FIFOMode;
    DMA_InitStruct.FIFOThreshold          = this->FIFOThreshold;
    DMA_InitStruct.MemBurst               = this->MemBurst;
    DMA_InitStruct.PeriphBurst            = this->PeriphBurst;

    LL_DMA_Init(this->DMAx, this->Stream, &DMA_InitStruct);

    LL_DMA_EnableStream(this->DMAx, this->Stream);
}


static DMA *build(DMA_TypeDef *DMAx, uint32_t PeriphOrM2MSrcAddress, uint32_t MemoryOrM2MDstAddress, uint32_t NbData,
                  uint32_t Channel, uint32_t Stream) {
    DMA *dma = malloc(sizeof(DMA));

    Driver_add(dma, GEN_PERM);

    dma->DMAx                   = DMAx;
    dma->PeriphOrM2MSrcAddress  = PeriphOrM2MSrcAddress;
    dma->MemoryOrM2MDstAddress  = MemoryOrM2MDstAddress;
    dma->Direction              = LL_DMA_DIRECTION_MEMORY_TO_MEMORY;
    dma->Mode                   = LL_DMA_MODE_NORMAL;
    dma->PeriphOrM2MSrcIncMode  = LL_DMA_PERIPH_INCREMENT;
    dma->MemoryOrM2MDstIncMode  = LL_DMA_MEMORY_INCREMENT;
    dma->PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
    dma->MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
    dma->NbData                 = NbData;
    dma->Channel                = Channel;
    dma->Priority               = LL_DMA_PRIORITY_MEDIUM;
    dma->FIFOMode               = LL_DMA_FIFOMODE_DISABLE;
    dma->FIFOThreshold          = LL_DMA_FIFOTHRESHOLD_1_4;
    dma->MemBurst               = LL_DMA_MBURST_SINGLE;
    dma->PeriphBurst            = LL_DMA_PBURST_SINGLE;
    dma->Stream                 = Stream;

    dma->init                   = &DMA_init;

    return dma;
}


const struct DMAClass DMABuilder = {.build = &build,};
